/************************************************************************/
/*                                                                      */
/*        Access Dallas 1-Wire Device with ATMEL AVRs                   */
/*                                                                      */
/*              Author: Peter Dannegger                                 */
/*                      danni@specs.de                                  */
/*                                                                      */
/* modified by Martin Thomas <eversmith@heizung-thomas.de> 9/2004       */
/************************************************************************/

#define THROUGH_2482 0
#define PARTIALLY_THROUGH_2482 1

//#if  !THROUGH_2482
#include <avr/io.h>
#include <avr/interrupt.h>
#include "delay.h"
//#endif

#if PARTIALLY_THROUGH_2482 || THROUGH_2482
#include "dallas.h"
#endif

#include "onewire.h"
#include "util.h"

#include "runtime_cfg.h"



#ifdef OW_ONE_BUS

#define OW_GET_IN()   ( OW_IN & (1<<OW_PIN))
#define OW_OUT_LOW()  ( OW_OUT &= (~(1 << OW_PIN)) )
#define OW_OUT_HIGH() ( OW_OUT |= (1 << OW_PIN) )
#define OW_DIR_IN()   ( OW_DDR &= (~(1 << OW_PIN )) )
#define OW_DIR_OUT()  ( OW_DDR |= (1 << OW_PIN) )

#else

/* set bus-config with ow_set_bus() */
uint8_t OW_PIN_MASK;
volatile uint8_t* OW_IN;
volatile uint8_t* OW_OUT;
volatile uint8_t* OW_DDR;

#define OW_GET_IN()   ( *OW_IN & OW_PIN_MASK )
#define OW_OUT_LOW()  ( *OW_OUT &= (uint8_t) ~OW_PIN_MASK )
#define OW_OUT_HIGH() ( *OW_OUT |= (uint8_t)  OW_PIN_MASK )
#define OW_DIR_IN()   ( *OW_DDR &= (uint8_t) ~OW_PIN_MASK )
#define OW_DIR_OUT()  ( *OW_DDR |= (uint8_t)  OW_PIN_MASK )
//#define OW_PIN2 PD6
//#define OW_DIR_IN2()   ( *OW_DDR &= ~(1 << OW_PIN2 ) )

void ow_set_bus(volatile uint8_t* in,
	volatile uint8_t* out,
	volatile uint8_t* ddr,
	uint8_t pin)
{
	OW_DDR=ddr;
	OW_OUT=out;
	OW_IN=in;
	OW_PIN_MASK=(1<<pin);
	ow_reset();
}

#endif

uint8_t ow_input_pin_state()
{
//#if 0 || THROUGH_2482

//#else
    return OW_GET_IN();
//#endif
}

void ow_parasite_enable(void)
{
    OW_OUT_HIGH();
	OW_DIR_OUT();
}

void ow_parasite_disable(void)
{
    OW_OUT_LOW();
	OW_DIR_IN();
}

uint8_t ow_reset(void)
{
#if 0 || THROUGH_2482
    return (dallasReset() == DALLAS_PRESENCE) ? 0 : 1;
#else

    uint8_t err;
    uint8_t sreg;

    OW_OUT_LOW(); // disable internal pull-up (maybe on from parasite)
    OW_DIR_OUT(); // pull OW-Pin low for 480us

    delay_us(480);

    sreg=SREG;
    cli();

    // set Pin as input - wait for clients to pull low
    OW_DIR_IN(); // input

    delay_us(66);
    err = OW_GET_IN();		// no presence detect
    // nobody pulled to low, still high

    SREG=sreg; // sei()

    // after a delay the clients should release the line
    // and input-pin gets back to high due to pull-up-resistor
    delay_us(480-66);
    if( OW_GET_IN() == 0 )		// short circuit
        err = 1;

    return err;
#endif
}

/* Timing issue when using runtime-bus-selection (!OW_ONE_BUS):
   The master should sample at the end of the 15-slot after initiating
   the read-time-slot. The variable bus-settings need more
   cycles than the constant ones so the delays had to be shortened
   to achive a 15uS overall delay
   Setting/clearing a bit in I/O Register needs 1 cyle in OW_ONE_BUS
   but around 14 cyles in configureable bus (us-Delay is 4 cyles per uS) */
uint8_t ow_bit_io( uint8_t b )
{
#if 0 || THROUGH_2482
    return ds2482BusTransferBit(b);
#else
    uint8_t sreg;

    sreg=SREG;
    cli();

    OW_DIR_OUT(); // drive bus low

    delay_us(2); // Recovery-Time wuffwuff was 1
    if ( b ) OW_DIR_IN(); // if bit is 1 set bus high (by ext. pull-up)

    // wuffwuff delay was 15uS-1 see comment above
    delay_us(15-2-OW_CONF_DELAYOFFSET);

    if( OW_GET_IN() == 0 ) b = 0;  // sample at end of read-timeslot

    delay_us(60-15);
    OW_DIR_IN();

    SREG=sreg; // sei();

    return b;
#endif
}


uint8_t ow_byte_wr( uint8_t b )
{
#if 0 || THROUGH_2482
    dallasWriteByte(b);
    return 0;
#else
	uint8_t i = 8, j;

	do {
		j = ow_bit_io( b & 1 );
		b >>= 1;
		if( j ) b |= 0x80;
	} while( --i );

        return b;
#endif
}


uint8_t ow_byte_rd( void )
{
#if PARTIALLY_THROUGH_2482 || THROUGH_2482
	
	if( onewire_2482_available )
	{

		//DEBUG_PUTS("dallas rd.. ");
		uint8_t o;
		if( dallasReadByte(&o) )
		{
			//DEBUG_PUTS("err\n");
		}
		else
		{
			//DEBUG_PUTS("ok\n");
		}
		return o;
	}
	else
	    return ow_byte_wr( 0xFF );
#else
    // read by sending 0xff (a dontcare?)
    return ow_byte_wr( 0xFF );
#endif
}


uint8_t ow_rom_search( uint8_t diff, uint8_t *id )
{
	uint8_t i, j, next_diff;
	uint8_t b;

	if( ow_reset() ) return OW_PRESENCE_ERR;	// error, no device found

	ow_byte_wr( OW_SEARCH_ROM );			// ROM search command
	next_diff = OW_LAST_DEVICE;			// unchanged on last device

	i = OW_ROMCODE_SIZE * 8;					// 8 bytes

	do {
		j = 8;					// 8 bits
		do {
			b = ow_bit_io( 1 );			// read bit
			if( ow_bit_io( 1 ) ) {			// read complement bit
				if( b )					// 11
				return OW_DATA_ERR;			// data error
			}
			else {
				if( !b ) {				// 00 = 2 devices
					if( diff > i || ((*id & 1) && diff != i) ) {
					b = 1;				// now 1
					next_diff = i;			// next pass 0
					}
				}
			}
			ow_bit_io( b );     			// write bit
			*id >>= 1;
			if( b ) *id |= 0x80;			// store bit

			i--;

		} while( --j );

		id++;					// next byte

	} while( i );

	return next_diff;				// to continue search
}


void ow_command( uint8_t command, uint8_t *id )
{
	uint8_t i;

	ow_reset();

	if( id ) {
		ow_byte_wr( OW_MATCH_ROM );			// to a single device
		i = OW_ROMCODE_SIZE;
		do {
			ow_byte_wr( *id );
			id++;
		} while( --i );
	}
	else {
		ow_byte_wr( OW_SKIP_ROM );			// to all devices
	}

	ow_byte_wr( command );
}
